package org.budo.accesslog.aop.interceptor;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.budo.accesslog.interceptor.AbstractAccesslogInterceptor;
import org.budo.dubbo.protocol.http.spring.web.BudoHandlerMethod;
import org.budo.support.lang.util.NumberUtil;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.mvcs.Mvcs;

/**
 * @author lmw
 */
public class BudoAccesslogAopInterceptor extends AbstractAccesslogInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        this.beforeAccess();

        Object proceed = null;

        Throwable error = null;
        try {
            proceed = invocation.proceed();
        } catch (Throwable e) {
            error = e;
        }

        Mvcs.setError(error);

        Boolean invocationIsHandler = this.invocationIsHandler(invocation);
        if (null != invocationIsHandler && invocationIsHandler) { // invocation is handler
            this.afterAccess();
        }

        this.throwError(error, invocation);

        return proceed;
    }

    private Boolean invocationIsHandler(MethodInvocation invocation) {
        Object handler = Mvcs.getHandler();
        if (null == handler) {
            return false;
        }

        if (!(handler instanceof BudoHandlerMethod)) {
            return false;
        }

        Method invocationMethod = invocation.getMethod(); // org.budo.warehouse.web.impl.DataNodeApiImpl.list(org.budo.support.dao.page.Page)

        BudoHandlerMethod budoHandlerMethod = (BudoHandlerMethod) handler; // org.budo.warehouse.web.api.DataNodeApi.list(org.budo.support.dao.page.Page)
        Class<?>[] handlerParameterTypes = budoHandlerMethod.getParameterTypes();

        // 方法签名匹配
        if (!NumberUtil.equals(handlerParameterTypes.length, invocationMethod.getParameterCount()) //
                || !StringUtil.equals(budoHandlerMethod.getMethodName(), invocationMethod.getName())) {
            return false;
        }

        // 类名匹配
        Class<?> invocationDeclaringClass = invocation.getMethod().getDeclaringClass();
        Class<?> invocationThisClass = invocation.getThis().getClass();

        if (StringUtil.equals(budoHandlerMethod.getInterfaceName(), invocationDeclaringClass.getName()) //
                || StringUtil.equals(budoHandlerMethod.getInterfaceName(), invocationThisClass.getName())) {
            return true;
        }

        // 子类匹配
        Class<?> methodInterface = ReflectUtil.classForNameOrNull(budoHandlerMethod.getInterfaceName());
        if (null != methodInterface && (methodInterface.isAssignableFrom(invocationDeclaringClass) //
                || methodInterface.isAssignableFrom(invocationThisClass))) {
            return true;
        }

        return false;
    }
}